namespace ScrumSprintMonitor.Wpf.Application
{
	#region #using Directives

	using System;
	using System.Collections.Generic;
	using System.Diagnostics.CodeAnalysis;
	using System.Globalization;
	using System.IO;
	using System.Reflection;
	using System.Windows.Forms;

	using AvailableTimeCalculator;

	using Implementation;
	using Implementation.AvailableTimeCalculator;
	using Implementation.Configuration;

	using Infrastructure;
	using Infrastructure.Configuration;
	using Infrastructure.IoC;

	using Microsoft.Practices.ServiceLocation;
	using Microsoft.Practices.Unity;

	using UI.Wpf;

	#endregion

	[SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses",
		Justification = "The UnityContainerAbstraction will create this class")]
	internal class UnityAppConfigurationDependentModule : StandardModule
	{
		/// <summary>
		///   Loads the module into the container.
		/// </summary>
		public override void Load()
		{
			SetupRuntimeBindings();
		}

		private void SetupRuntimeBindings()
		{
		    var connection = Container.Resolve<IConnectionProvider>().ProvideConnectionTo(new Uri(Settings.Default.TfsServerUri),
		                                                                                  Settings.Default.TfsServerUserName,
		                                                                                  Settings.Default.TfsServerPassword, true);

            Container.Resolve<ILogger<UnityAppConfigurationDependentModule>>().Info("Connecting to server at '{0}'.", Settings.Default.TfsServerUri);

		    Container.RegisterType<IReportableData, ReportableData>(new ContainerControlledLifetimeManager(),
		                                                            new InjectionConstructor(new ResolvedParameter<IServiceLocator>(),
		                                                                                     new InjectionParameter<IConnection>(connection),
		                                                                                     new ResolvedParameter<ISprintConfiguration>()));
            Container.RegisterType<ISprint, Sprint>(new InjectionMethod("Initialize", connection));
            Container.RegisterType<ISprintBurndown, SprintBurndown>();
            Container.RegisterType<ITeamMember, TeamMember>();
            Container.RegisterType<ITeamMemberRemainingWorkload, TeamMemberRemainingWorkload>();
            Container.RegisterType<IRemainingWorkStatusEvaluator, RemainingWorkStatusEvaluator>(new ContainerControlledLifetimeManager());

			var workItemInterpretationConfiguration = new WorkItemInterpretationConfiguration
			                                          	{
			                                          		AllowUnestimatedWorkItems =
			                                          			!Settings.Default.ReportUnestimatedSprintBacklogItems
			                                          	};
            Container.RegisterInstance<IWorkItemInterpretationConfiguration>(workItemInterpretationConfiguration);

			var shellLayoutConfiguration = new ShellLayoutConfiguration(Settings.Default.UseMultipleMonitors && SystemInformation.MonitorCount > 1,
			                                                            !Bootstrapper.Debug, Settings.Default.ShowBurndownChart,
			                                                            Settings.Default.ActiveSprintMode);
            Container.RegisterInstance<IShellLayoutConfiguration>(shellLayoutConfiguration);

			SetupConfigurationBindings();
			SetupPresentationSettingsBindings();
			SetupTeamInformationBindings();

            Container.RegisterType<IFreeBusyInfoConfiguration, FreeBusyInfoConfiguration>(new ContainerControlledLifetimeManager());
			SetupFreeBusyInfoProvider();

            Container.RegisterType<PowerManager>(new ContainerControlledLifetimeManager());
		}

		private void SetupFreeBusyInfoProvider()
		{
			bool setup = false;

			if (Settings.Default.EnableOutlookIntegration)
			{
                var teamMemberOutlookFreeBusyInfo = new TeamMemberOutlookFreeBusyInfo(Container.Resolve<IFreeBusyInfoConfiguration>(),
                                                                                      Container.Resolve<ILogger<TeamMemberOutlookFreeBusyInfo>>());
				setup = SetupFreeBusyInfoProvider(teamMemberOutlookFreeBusyInfo, teamMemberOutlookFreeBusyInfo.SetUp);
			}

			if (!setup)
			{
                var teamMemberFreeBusyInfo = new TeamMemberFreeBusyInfo(Container.Resolve<IFreeBusyInfoConfiguration>());

				SetupFreeBusyInfoProvider(teamMemberFreeBusyInfo, () => { });
			}

			if (Settings.Default.EnableOutlookIntegration)
			{
                Container.RegisterType<ITeamMemberAvailableTimeCalculator, FreeBusyTeamMemberAvailableTimeCalculator>(new ContainerControlledLifetimeManager());
			}
			else
			{
                Container.RegisterType<ITeamMemberAvailableTimeCalculator, SimpleTeamMemberAvailableTimeCalculator>(new ContainerControlledLifetimeManager());
			}
		}

		private bool SetupFreeBusyInfoProvider(ITeamMemberFreeBusyInfo freeBusyInfo, Action setupFreeBusyInfoProvider)
		{
			try
			{
				setupFreeBusyInfoProvider();

                Container.RegisterInstance<ITeamMemberFreeBusyInfo>(freeBusyInfo);

				return true;
			}
			catch (ConnectionException)
			{
				var disposable = freeBusyInfo as IDisposable;
				if (disposable != null)
				{
					disposable.Dispose();
				}

				if (freeBusyInfo is TeamMemberFreeBusyInfo)
				{
					throw;
				}

				return false;
			}
		}

		private static string GetFullPath(string filePath)
		{
			if (!Path.IsPathRooted(filePath))
			{
				string homeDirectoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
				filePath = Path.Combine(homeDirectoryName, filePath);
			}
			return Path.GetFullPath(filePath);
		}

		private void SetupPresentationSettingsBindings()
		{
			string fullPath = GetFullPath(Settings.Default.PresentationConfigurationFilePath);

			if (!File.Exists(fullPath))
			{
				throw new FileNotFoundException(
					string.Format(CultureInfo.CurrentCulture, "Could not find the presentation settings file at {0}.",
					              Settings.Default.PresentationConfigurationFilePath), fullPath);
			}

		    var fileDataStreamProvider = Container.Resolve<FileDataStreamProvider>(new ParameterOverride("filePath", fullPath));
		    Container.RegisterType<IPresentationConfiguration, StreamBasedPresentationConfiguration>(new ContainerControlledLifetimeManager(),
		                                                                                             new InjectionConstructor(
		                                                                                                 new InjectionParameter<IDataStreamProvider>(
		                                                                                                     fileDataStreamProvider)));
		}

		private void SetupConfigurationBindings()
		{
			string fullPath = GetFullPath(Settings.Default.ConfigurationFilePath);

			if (!File.Exists(fullPath))
			{
				throw new FileNotFoundException(
					string.Format(CultureInfo.CurrentCulture, "Could not find the configuration file at {0}.", Settings.Default.ConfigurationFilePath), fullPath);
			}

		    var fileDataStreamProvider = Container.Resolve<FileDataStreamProvider>(new ParameterOverride("filePath", fullPath));
		    Container.RegisterType<ISprintConfiguration, StreamBasedSprintConfiguration>(new ContainerControlledLifetimeManager(),
		                                                                                 new InjectionConstructor(
		                                                                                     new InjectionParameter<IDataStreamProvider>(
		                                                                                         fileDataStreamProvider),
		                                                                                     new ResolvedParameter<ILogger<StreamBasedSprintConfiguration>>()));
		}

		private void SetupTeamInformationBindings()
		{
			string fullPath = GetFullPath(Settings.Default.TeamMembersConfigurationFilePath);

			if (!File.Exists(fullPath))
			{
				throw new FileNotFoundException(
					string.Format(CultureInfo.CurrentCulture, "Could not find the team information xml file at {0}.",
					              Settings.Default.TeamMembersConfigurationFilePath), fullPath);
			}

			IDataStreamProvider xsdStreamProvider = null;
			string xsdPath = Path.Combine(Path.GetDirectoryName(Settings.Default.TeamMembersConfigurationFilePath), @"team-info.xsd");
			if (!File.Exists(xsdPath))
			{
				xsdPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @"team-info.xsd");
			}
			if (File.Exists(xsdPath))
			{
                xsdStreamProvider = Container.Resolve<FileDataStreamProvider>(new ParameterOverride("filePath", xsdPath));
			}

		    var xmlStreamProvider = Container.Resolve<FileDataStreamProvider>(new ParameterOverride("filePath", fullPath));
		    var teamMemberInfoProviderParams = new InjectionConstructor(new ResolvedParameter<IUnityContainer>(),
		                                                                new InjectionParameter<IDataStreamProvider>(xmlStreamProvider),
		                                                                new InjectionParameter<IDataStreamProvider>(xsdStreamProvider),
		                                                                new ResolvedParameter<ILogger<XmlStreamBasedTeamInfoProvider>>());

            Container.RegisterType<ITeamMemberInfoProvider, XmlStreamBasedTeamInfoProvider>(new ContainerControlledLifetimeManager(), teamMemberInfoProviderParams);

            Container.RegisterType<ITeamMemberInformation, TeamMemberInformation>();
		}
	}
}